<?php
/**
 * GigatecLdap Class
 *
 * @link http://www.gigatec.de
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 * @version $Id$
 */
class GigatecLdap {

	private $enableLogging = FALSE;
	
	private $serverUrl = NULL;
	private $userIdField = 'uid';
	private $baseDn = NULL;
	private $memberOf = NULL;
	private $adminUser = NULL;
	private $adminPass = NULL;
	private $usernameSuffix = NULL;
	private $filter = NULL;
	private $kerberos = NULL;

	private $ldapconn = NULL;

	public function authenticate($username, $password) {

		if(empty($password)) {
			return false;
		}
		
		$this->log("authenticate(".$username.", ...) start");
		
		$success = false;
		
		$this->validate();

		if (empty($username)) {
			throw new Exception('username is not set');
		}

		$this->connect();

		$result = $this->getUserEntries($username);
		$this->log("authenticate(".$username.", ...) getUserEntries: ".json_encode($result));
		if ($result['count'] > 0 && $result[0]['dn']) {
			$success = @ldap_bind($this->ldapconn, $result[0]['dn'], $password);
			$this->log("authenticate(".$username.", ...) ldap_bind (" . $result[0]['dn'] . "): " . ($success ? 'success' : 'fail'));
			$memberOfList = @$result[0]['memberof'];
			if ($success && !empty($this->memberOf) && !empty($memberOfList)) {
				$success = false;
				for ($i = 0; $i < $memberOfList['count']; $i++) {
					if ($memberOfList[$i] == $this->memberOf) {
						$success = true;
					}
				}
				$this->log("authenticate(".$username.", ...) check memberOf (".$this->memberOf."): " . ($success ? 'success' : 'fail'));
			}
		}

		$this->close();
		
		return $success;
	}
	
	public function kerbthenticate($username) {
		
		if (isset($_SERVER["REMOTE_USER"])) 
		{
			$this->log("REMOTE_USER: ".$_SERVER["REMOTE_USER"]);
		}
		$this->log("kerbthenticate(".$username.") start");
		
		$success = false;
		
		$this->validate();

		if (empty($username)) {
			throw new Exception('username is not set');
		}

		$this->connect();

		$result = $this->getUserEntries($username);
		$this->log("authenticate(".$username.") getUserEntries: ".json_encode($result));
		if ($result['count'] > 0 && $result[0]['dn']) {
			$success = true;
			$memberOfList = @$result[0]['memberof'];
			if (!empty($this->memberOf) && !empty($memberOfList)) {
				$success = false;
				for ($i = 0; $i < $memberOfList['count']; $i++) {
					if ($memberOfList[$i] == $this->memberOf) {
						$success = true;
					}
				}
				$this->log("authenticate(".$username.") check memberOf (".$this->memberOf."): " . ($success ? 'success' : 'fail'));
			}
		}

		$this->close();
		
		return $success;
	}

	public function getUser($username, $aliasField = 'cn', $mailField = 'mail') {
		
		$user = array();
		
		$this->validate();
		
		if (empty($username)) {
			throw new Exception('username is not set');
		}
		
		$this->connect();

		$result = $this->getUserEntries($username);
		if ($result['count'] > 0) {
			$user['username'] = $username;
			$user['alias'] = $result[0][$aliasField][0];
			$user['mail'] = $result[0][$mailField][0];
		}
		
		$this->close();
		
		return $user;
	}

	public function setServerUrl($serverUrl) {
		$this->serverUrl = $serverUrl;
	}

	public function setUserIdField($userIdField) {
		$this->userIdField = $userIdField;
	}

	public function setBaseDn($baseDn) {
		$this->baseDn = $baseDn;
	}

	public function setMemberOf($memberOf) {
		$this->memberOf = $memberOf;
	}
	
	public function setAdminUser($adminUser) {
		$this->adminUser = $adminUser;
	}
	
	public function setAdminPass($adminPass) {
		$this->adminPass = $adminPass;
	}
	
	public function setUsernameSuffix($usernameSuffix) {
		$this->usernameSuffix = $usernameSuffix;
	}
	
	public function setFilter($filter) {
		$this->filter = $filter;
	}
	
	public function setKerberos($kerberos) {
		$this->kerberos = $kerberos;
	}

	private function validate() {

		if (empty($this->serverUrl)) {
			throw new Exception('serverUrl is not set');
		}
		if (empty($this->userIdField)) {
			throw new Exception('userIdField is not set');
		}
		if (empty($this->baseDn)) {
			throw new Exception('baseDn is not set');
		}
	}
	
	private function connect() {

		$ldapconn = @ldap_connect($this->serverUrl);
		$this->log("ldap_connect(".$this->serverUrl.")");

		if(!$ldapconn) {
			$this->log("ldap_connect() FAILED!");
			throw new Exception('could not connect to ldap server');
		}

		ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
		ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
		
		$this->ldapconn = $ldapconn;
	}
	
	private function getUserEntries($username) {
		
		$this->log("getUserEntries(".$username.", ...) ldap_bind: ".$this->addSuffix($this->adminUser));
		if (!empty($this->adminUser) && !@ldap_bind($this->ldapconn, $this->addSuffix($this->adminUser), $this->adminPass)) {
			$this->log("ldap_bind() as admin FAILED!");
			throw new Exception('cound not bind as admin');
		}
		
		if (!empty($this->filter)) {
			$searchFilter = "(&".$this->filter."(".$this->userIdField."=".$this->addSuffix($username)."))";
		} else {
			$searchFilter = $this->userIdField."=".$this->addSuffix($username);
		}
		$this->log("getUserEntries(".$username.", ...) ldap_search: ".$searchFilter);
		$search = @ldap_search($this->ldapconn, $this->baseDn, $searchFilter);
		if($search)	{
			$this->log("ldap_get_entries(); count: ".count($search));
			return @ldap_get_entries($this->ldapconn, $search);
		} else {
			$this->log("ldap_get_entries() FAILED!");
			throw new Exception('could not get user entries, maybe anonymous bind is forbidden or admin credentials are invalid');
		}
		return array('count' => 0);
	}
	
	private function addSuffix($username) {
		
		if (!empty($this->usernameSuffix)) {
			$username .= $this->usernameSuffix;
		}
		return $username;
	}

	private function close() {
		$this->log("ldap_close()");
		@ldap_close($this->ldapconn);
	}
	
	private function log($text) {
		if ($this->enableLogging) {
			$f = fopen('gigatec_ldap.log', 'a');
			if ($f != NULL) {
				fwrite($f, strftime('%F %T') . ": $text\n");
				fclose($f);
			}
		}
	}
}

?>
